package com.tianji.promotion.handler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.service.ICouponService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 优惠券定时发放任务处理器
 * 
 * @author Yandel
 * @since 2025-08-15
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CouponJobHandler {

    private final ICouponService couponService;

    /**
     * 定时发放优惠券任务
     * 将状态为"未开始"且发放时间早于当前时间的优惠券状态更新为"进行中"
     * 使用XXL-JOB数据分片功能，避免重复处理数据
     */
    @XxlJob("couponIssueJob")
    public void issueCoupons() {
        log.info("开始执行优惠券定时发放任务");
        
        try {
            // 1. 获取分片信息，用于数据分片处理
            int shardIndex = XxlJobHelper.getShardIndex();
            int shardTotal = XxlJobHelper.getShardTotal();
            
            log.info("当前分片索引: {}, 总分片数: {}", shardIndex, shardTotal);
            
            // 2. 使用分页查询，避免一次性加载所有数据
            int pageSize = 100; // 每页处理100条记录
            int pageNo = shardIndex + 1; // 分片索引+1作为起始页码
            int totalProcessed = 0;
            int totalSkipped = 0;
            int totalError = 0;
            
            while (true) {
                // 查询当前分页的数据
                Page<Coupon> page = new Page<>(pageNo, pageSize);
                Page<Coupon> result = couponService.lambdaQuery()
                        .eq(Coupon::getStatus, CouponStatus.UN_ISSUE)
                        .le(Coupon::getIssueBeginTime, LocalDateTime.now())
                        .orderByAsc(Coupon::getId) // 确保分片处理的一致性
                        .page(page);
                
                List<Coupon> coupons = result.getRecords();
                if (coupons.isEmpty()) {
                    log.info("第 {} 页没有数据，结束处理", pageNo);
                    break;
                }
                
                log.info("处理第 {} 页，共 {} 张优惠券", pageNo, coupons.size());
                
                // 3. 处理当前页的数据
                int processedCount = 0;
                int skippedCount = 0;
                int errorCount = 0;
                
                for (Coupon coupon : coupons) {
                    try {
                        // 4. 更新优惠券状态为"进行中"
                        // 使用乐观锁机制，再次确认状态，避免并发问题
                        boolean updated = couponService.lambdaUpdate()
                                .eq(Coupon::getId, coupon.getId())
                                .eq(Coupon::getStatus, CouponStatus.UN_ISSUE) // 再次确认状态，避免并发问题
                                .set(Coupon::getStatus, CouponStatus.ISSUING)
                                .set(Coupon::getUpdateTime, LocalDateTime.now())
                                .update();
                        
                        if (updated) {
                            processedCount++;
                            log.info("成功发放优惠券: id={}, name={}, 发放时间={}", 
                                    coupon.getId(), coupon.getName(), coupon.getIssueBeginTime());
                        } else {
                            skippedCount++;
                            log.warn("优惠券状态已变更，跳过处理: id={}, name={}", 
                                    coupon.getId(), coupon.getName());
                        }
                    } catch (Exception e) {
                        errorCount++;
                        log.error("处理优惠券发放失败: id={}, name={}", 
                                coupon.getId(), coupon.getName(), e);
                    }
                }
                
                totalProcessed += processedCount;
                totalSkipped += skippedCount;
                totalError += errorCount;
                
                log.info("第 {} 页处理完成，成功: {}, 跳过: {}, 失败: {}", 
                        pageNo, processedCount, skippedCount, errorCount);
                
                // 下一页的页码 = 当前页码 + 总分片数，确保不同分片处理不同页的数据
                pageNo += shardTotal;
                
                // 如果当前页的数据量小于页大小，说明已经是最后一页
                if (coupons.size() < pageSize) {
                    break;
                }
            }
            
            log.info("优惠券定时发放任务执行完成，当前分片总共处理了 {} 张优惠券，跳过 {} 张，失败 {} 张", 
                    totalProcessed, totalSkipped, totalError);
            
        } catch (Exception e) {
            log.error("优惠券定时发放任务执行异常", e);
            throw e; // 重新抛出异常，让XXL-JOB知道任务执行失败
        }
    }

    /**
     * 定时结束发放优惠券任务
     * 将状态为"进行中"且发放结束时间早于当前时间的优惠券状态更新为"发放结束"
     * 使用XXL-JOB数据分片功能，避免重复处理数据
     */
    @XxlJob("couponFinishJob")
    public void finishCoupons() {
        log.info("开始执行优惠券定时结束发放任务");
        
        try {
            // 1. 获取分片信息，用于数据分片处理
            int shardIndex = XxlJobHelper.getShardIndex();
            int shardTotal = XxlJobHelper.getShardTotal();
            
            log.info("当前分片索引: {}, 总分片数: {}", shardIndex, shardTotal);
            
            // 2. 使用分页查询，避免一次性加载所有数据
            int pageSize = 100; // 每页处理100条记录
            int pageNo = shardIndex + 1; // 分片索引+1作为起始页码
            int totalProcessed = 0;
            int totalSkipped = 0;
            int totalError = 0;
            
            while (true) {
                // 查询当前分页的数据
                Page<Coupon> page = new Page<>(pageNo, pageSize);
                Page<Coupon> result = couponService.lambdaQuery()
                        .eq(Coupon::getStatus, CouponStatus.ISSUING)
                        .le(Coupon::getIssueEndTime, LocalDateTime.now())
                        .orderByAsc(Coupon::getId) // 确保分片处理的一致性
                        .page(page);
                
                List<Coupon> coupons = result.getRecords();
                if (coupons.isEmpty()) {
                    log.info("第 {} 页没有数据，结束处理", pageNo);
                    break;
                }
                
                log.info("处理第 {} 页，共 {} 张优惠券", pageNo, coupons.size());
                
                // 3. 处理当前页的数据
                int processedCount = 0;
                int skippedCount = 0;
                int errorCount = 0;
                
                for (Coupon coupon : coupons) {
                    try {
                        // 4. 更新优惠券状态为"发放结束"
                        // 使用乐观锁机制，再次确认状态，避免并发问题
                        boolean updated = couponService.lambdaUpdate()
                                .eq(Coupon::getId, coupon.getId())
                                .eq(Coupon::getStatus, CouponStatus.ISSUING) // 再次确认状态，避免并发问题
                                .set(Coupon::getStatus, CouponStatus.FINISHED)
                                .set(Coupon::getUpdateTime, LocalDateTime.now())
                                .update();
                        
                        if (updated) {
                            processedCount++;
                            log.info("成功结束发放优惠券: id={}, name={}, 结束时间={}", 
                                    coupon.getId(), coupon.getName(), coupon.getIssueEndTime());
                        } else {
                            skippedCount++;
                            log.warn("优惠券状态已变更，跳过处理: id={}, name={}", 
                                    coupon.getId(), coupon.getName());
                        }
                    } catch (Exception e) {
                        errorCount++;
                        log.error("处理优惠券结束发放失败: id={}, name={}", 
                                coupon.getId(), coupon.getName(), e);
                    }
                }
                
                totalProcessed += processedCount;
                totalSkipped += skippedCount;
                totalError += errorCount;
                
                log.info("第 {} 页处理完成，成功: {}, 跳过: {}, 失败: {}", 
                        pageNo, processedCount, skippedCount, errorCount);
                
                // 下一页的页码 = 当前页码 + 总分片数，确保不同分片处理不同页的数据
                pageNo += shardTotal;
                
                // 如果当前页的数据量小于页大小，说明已经是最后一页
                if (coupons.size() < pageSize) {
                    break;
                }
            }
            
            log.info("优惠券定时结束发放任务执行完成，当前分片总共处理了 {} 张优惠券，跳过 {} 张，失败 {} 张", 
                    totalProcessed, totalSkipped, totalError);
            
        } catch (Exception e) {
            log.error("优惠券定时结束发放任务执行异常", e);
            throw e; // 重新抛出异常，让XXL-JOB知道任务执行失败
        }
    }
}
